/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */

import type { TypedMessage } from "@fluidframework/core-interfaces/internal";
import type {
	ITree,
	ISignalMessage,
	ISequencedDocumentMessage,
} from "@fluidframework/driver-definitions/internal";

/**
 * An envelope wraps the contents with the intended target
 * @legacy @beta
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO (#28746): breaking change
export interface IEnvelope<TContents = any> {
	/**
	 * The target for the envelope
	 */
	address: string;

	/**
	 * The contents of the envelope
	 */
	contents: TContents;
}

/**
 * Represents ISignalMessage with its type.
 * @legacy @beta
 */
export interface IInboundSignalMessage<TMessage extends TypedMessage = TypedMessage>
	extends ISignalMessage<TMessage> {
	readonly type: TMessage["type"];
}

/**
 * Message send by client attaching local data structure.
 * Contains snapshot of data structure which is the current state of this data structure.
 * @legacy @beta
 */
export interface IAttachMessage {
	/**
	 * The identifier for the object
	 */
	id: string;

	/**
	 * The type of object
	 */
	type: string;

	/**
	 * Initial snapshot of the document (contains ownership)
	 */
	snapshot: ITree;
}

/**
 * This type should be used when reading an incoming attach op,
 * but it should not be used when creating a new attach op.
 * Older versions of attach messages could have null snapshots,
 * so this gives correct typings for writing backward compatible code.
 * @legacy @beta
 */
export type InboundAttachMessage = Omit<IAttachMessage, "snapshot"> & {
	// eslint-disable-next-line @rushstack/no-new-null -- TODO: breaking change; protocol might even explicitly use null
	snapshot: IAttachMessage["snapshot"] | null;
};

/**
 * This is the message type that is used within the runtime when processing a sequenced message.
 * It is the same as ISequencedDocumentMessage, but without the contents and clientSequenceNumbers
 * which are sent separately. The contents are modified at multiple layers in the stack so having it
 * separate doesn't require packing and unpacking the entire message.
 * @legacy @beta
 */
export type ISequencedMessageEnvelope = Omit<
	ISequencedDocumentMessage,
	"contents" | "clientSequenceNumber"
>;

/**
 * These are the contents of a runtime message as it is processed throughout the stack.
 * @legacy @beta
 * @sealed
 */
export interface IRuntimeMessagesContent {
	/**
	 * The contents of the message, i.e., the payload
	 */
	readonly contents: unknown;
	/**
	 * The local metadata associated with the original message that was submitted
	 */
	readonly localOpMetadata: unknown;
	/**
	 * The client sequence number of the message
	 */
	readonly clientSequenceNumber: number;
}

/**
 * A collection of messages that are processed by the runtime.
 * @legacy @beta
 * @sealed
 */
export interface IRuntimeMessageCollection {
	/**
	 * The envelope for all the messages in the collection
	 */
	readonly envelope: ISequencedMessageEnvelope;
	/**
	 * Whether these messages were originally generated by the client processing them
	 */
	readonly local: boolean;
	/**
	 * The contents of the messages in the collection
	 */
	readonly messagesContent: readonly IRuntimeMessagesContent[];
}

/**
 * Outgoing {@link IFluidDataStoreChannel} message structures.
 * @internal
 *
 * @privateRemarks
 * Future use opportunity:
 * - Change {@link IFluidDataStoreChannel} and {@link IFluidParentContext},
 * to have a generic specifying `T extends FluidDataStoreMessage` and uses
 * `T["type"]` and `T["content"]` to qualify message related methods,
 * preferably where `submitMessage`, `reSubmit`, and `rollback` have
 * overloads to ensure callers pair values correctly.
 * - A further improvement would be to reshape `submitMessage`, `reSubmit`,
 * and `rollback` to accept `T` as `message` parameter instead of `type`
 * and `content` parameters that are hard to convince TypeScript must be
 * paired in implementations.
 * - Caveat to enhanced type safety is that a user that changes their own
 * `FluidDataStoreMessage` definition over time needs to account for
 * protocol changes. So `unknown` should continue to be used for incoming
 * message methods (where messages are not known to originate locally).
 */
export interface FluidDataStoreMessage {
	type: string;
	content: unknown;
}

/**
 * Interface to provide access to snapshot blobs to DataStore layer.
 *
 * @legacy @beta
 */
export interface IRuntimeStorageService {
	/**
	 * Reads the object with the given ID, returns content in arrayBufferLike
	 */
	readBlob(id: string): Promise<ArrayBufferLike>;
}
